package com.lzb.compressmaster.utils;

import android.content.Context;
import android.util.Log;

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream;
import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream;
import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.Deflater;

public class ApacheCompressUtil {

    private static final String TAG = "ApacheCompressUtil";

    public enum CompressFormat {
        ZIP,           // ZIP格式
        SEVEN_Z,       // 7-Zip格式
        TAR,           // TAR格式
        GZIP,          // GZIP格式
        BZIP2,         // BZIP2格式
        XZ,            // XZ格式
        AR,            // AR格式
        CPIO,          // CPIO格式
        JAR            // JAR格式
    }

    public enum CompressMode {
        FAST, SMALL
    }

    public interface CompressCallback {
        void onProgress(int progress);
        void onSuccess(String outputPath);
        void onError(String error);
    }

    /**
     * 压缩文件或文件夹（文件路径列表版本）
     */
    public static void compressFilesFromPaths(Context context, List<String> filePaths, File outputFile,
                                            CompressFormat format, CompressMode mode, CompressCallback callback) {
        
        new Thread(() -> {
            try {
                // 将字符串路径转换为File对象列表
                List<File> files = new ArrayList<>();
                for (String path : filePaths) {
                    if (path != null && !path.trim().isEmpty()) {
                        files.add(new File(path.trim()));
                    }
                }
                
                // 调用原有的File列表方法
                compressFiles(context, files, outputFile, format, mode, callback);
                
            } catch (Exception e) {
                Log.e(TAG, "压缩失败", e);
                if (callback != null) {
                    callback.onError("压缩失败: " + e.getMessage());
                }
            }
        }).start();
    }

    /**
     * 压缩文件或文件夹（File对象列表版本）
     */
    public static void compressFiles(Context context, List<File> files, File outputFile,
                                   CompressFormat format, CompressMode mode, CompressCallback callback) {
        
        new Thread(() -> {
            try {
                switch (format) {
                    case ZIP:
                        compressToZip(files, outputFile, mode, callback);
                        break;
                    case SEVEN_Z:
                        compressTo7z(files, outputFile, mode, callback);
                        break;
                    case TAR:
                        compressToTar(files, outputFile, callback);
                        break;
                    case GZIP:
                        compressToGzip(files, outputFile, mode, callback);
                        break;
                    case BZIP2:
                        compressToBzip2(files, outputFile, mode, callback);
                        break;
                    case XZ:
                        compressToXz(files, outputFile, mode, callback);
                        break;
                    case AR:
                        compressToAr(files, outputFile, callback);
                        break;
                    case CPIO:
                        compressToCpio(files, outputFile, callback);
                        break;
                    case JAR:
                        compressToJar(files, outputFile, callback);
                        break;
                }
            } catch (Exception e) {
                Log.e(TAG, "压缩失败", e);
                if (callback != null) {
                    callback.onError("压缩失败: " + e.getMessage());
                }
            }
        }).start();
    }

    /**
     * ZIP格式压缩
     */
    private static void compressToZip(List<File> files, File outputFile, CompressMode mode, 
                                    CompressCallback callback) throws Exception {
        ensureOutputDir(outputFile);
        
        try (FileOutputStream fos = new FileOutputStream(outputFile);
             ZipArchiveOutputStream zos = new ZipArchiveOutputStream(fos)) {
            zos.setLevel(mode == CompressMode.FAST ? Deflater.BEST_SPEED : Deflater.BEST_COMPRESSION);
            compressFilesToArchive(zos, files, callback);
        }
    }

    /**
     * 7Z格式压缩
     */
    private static void compressTo7z(List<File> files, File outputFile, CompressMode mode, 
                                   CompressCallback callback) throws Exception {
        ensureOutputDir(outputFile);
        
        try (SevenZOutputFile sevenZOutput = new SevenZOutputFile(outputFile)) {
            compressFilesTo7z(files, sevenZOutput, callback);
        }
    }

    /**
     * TAR格式压缩
     */
    private static void compressToTar(List<File> files, File outputFile, 
                                    CompressCallback callback) throws Exception {
        ensureOutputDir(outputFile);
        
        try (FileOutputStream fos = new FileOutputStream(outputFile);
             TarArchiveOutputStream tos = new TarArchiveOutputStream(fos)) {
            tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            compressFilesToArchive(tos, files, callback);
        }
    }

    /**
     * GZIP格式压缩（单个文件）
     */
    private static void compressToGzip(List<File> files, File outputFile, CompressMode mode,
                                     CompressCallback callback) throws Exception {
        ensureOutputDir(outputFile);
        
        if (files.size() != 1 || files.get(0).isDirectory()) {
            throw new IllegalArgumentException("GZIP格式只支持单个文件压缩");
        }
        
        try (FileInputStream fis = new FileInputStream(files.get(0));
             FileOutputStream fos = new FileOutputStream(outputFile);
             GzipCompressorOutputStream gzos = new GzipCompressorOutputStream(fos)) {
            
            byte[] buffer = new byte[8192];
            int length;
            long totalBytes = files.get(0).length();
            long processedBytes = 0;
            
            while ((length = fis.read(buffer)) > 0) {
                gzos.write(buffer, 0, length);
                processedBytes += length;
                
                if (callback != null) {
                    int progress = (int) ((processedBytes * 100) / totalBytes);
                    callback.onProgress(progress);
                }
            }
            
            if (callback != null) {
                callback.onSuccess("压缩完成");
            }
        }
    }

    /**
     * BZIP2格式压缩（单个文件）
     */
    private static void compressToBzip2(List<File> files, File outputFile, CompressMode mode,
                                        CompressCallback callback) throws Exception {
        ensureOutputDir(outputFile);
        
        if (files.size() != 1 || files.get(0).isDirectory()) {
            throw new IllegalArgumentException("BZIP2格式只支持单个文件压缩");
        }
        
        try (FileInputStream fis = new FileInputStream(files.get(0));
             FileOutputStream fos = new FileOutputStream(outputFile);
             BZip2CompressorOutputStream bzos = new BZip2CompressorOutputStream(fos)) {
            
            byte[] buffer = new byte[8192];
            int len;
            long totalSize = files.get(0).length();
            long processedSize = 0;
            
            while ((len = fis.read(buffer)) != -1) {
                bzos.write(buffer, 0, len);
                processedSize += len;
                
                if (callback != null && totalSize > 0) {
                    int progress = (int) ((processedSize * 100) / totalSize);
                    callback.onProgress(Math.min(progress, 100));
                }
            }

            if (callback != null) {
                callback.onSuccess("压缩完成");
            }
        }
    }

    /**
     * XZ格式压缩（单个文件）
     */
    private static void compressToXz(List<File> files, File outputFile, CompressMode mode,
                                   CompressCallback callback) throws Exception {
        ensureOutputDir(outputFile);
        
        if (files.size() != 1 || files.get(0).isDirectory()) {
            throw new IllegalArgumentException("XZ格式只支持单个文件压缩");
        }
        
        try (FileInputStream fis = new FileInputStream(files.get(0));
             FileOutputStream fos = new FileOutputStream(outputFile);
             XZCompressorOutputStream xzOs = new XZCompressorOutputStream(fos)) {
            
            byte[] buffer = new byte[8192];
            int len;
            long totalSize = files.get(0).length();
            long processedSize = 0;
            
            while ((len = fis.read(buffer)) != -1) {
                xzOs.write(buffer, 0, len);
                processedSize += len;
                
                if (callback != null && totalSize > 0) {
                    int progress = (int) ((processedSize * 100) / totalSize);
                    callback.onProgress(Math.min(progress, 100));
                }
            }
            
            if (callback != null) {
                callback.onSuccess("压缩完成");
            }
        }
    }

    /**
     * AR格式压缩
     */
    private static void compressToAr(List<File> files, File outputFile, 
                                   CompressCallback callback) throws Exception {
        ensureOutputDir(outputFile);
        
        try (FileOutputStream fos = new FileOutputStream(outputFile);
             ArArchiveOutputStream aos = new ArArchiveOutputStream(fos)) {
            compressFilesToArchive(aos, files, callback);
        }
    }

    /**
     * CPIO格式压缩
     */
    private static void compressToCpio(List<File> files, File outputFile, 
                                     CompressCallback callback) throws Exception {
        ensureOutputDir(outputFile);
        
        try (FileOutputStream fos = new FileOutputStream(outputFile);
             CpioArchiveOutputStream cos = new CpioArchiveOutputStream(fos)) {
            compressFilesToArchive(cos, files, callback);
        }
    }

    /**
     * JAR格式压缩
     */
    private static void compressToJar(List<File> files, File outputFile, 
                                    CompressCallback callback) throws Exception {
        ensureOutputDir(outputFile);
        
        try (FileOutputStream fos = new FileOutputStream(outputFile);
             JarArchiveOutputStream jos = new JarArchiveOutputStream(fos)) {
            compressFilesToArchive(jos, files, callback);
        }
    }

    /**
     * 通用压缩方法
     */
    private static void compressFilesToArchive(ArchiveOutputStream aos, List<File> files, 
                                             CompressCallback callback) throws Exception {
        long totalSize = getTotalSize(files);
        long processedSize = 0;

        for (File file : files) {
            if (file != null && file.exists()) {
                if (file.isDirectory()) {
                    addFolderToArchive(aos, file, file.getName(), totalSize, processedSize, callback);
                } else {
                    addFileToArchive(aos, file, file.getName(), totalSize, processedSize, callback);
                }
                processedSize += file.isDirectory() ? getFolderSize(file) : file.length();
            }
        }

        aos.finish();
        
        if (callback != null) {
            callback.onSuccess("压缩完成");
        }
    }

    private static void addFileToArchive(ArchiveOutputStream aos, File file, String entryName,
                                       long totalSize, long processedSize, CompressCallback callback) throws Exception {
        ArchiveEntry entry = aos.createArchiveEntry(file, entryName);
        aos.putArchiveEntry(entry);

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
            byte[] buffer = new byte[8192];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                aos.write(buffer, 0, len);
                processedSize += len;
                
                if (totalSize > 0 && callback != null) {
                    int progress = (int) ((processedSize * 100) / totalSize);
                    callback.onProgress(Math.min(progress, 100));
                }
            }
        }
        aos.closeArchiveEntry();
    }

    private static void addFolderToArchive(ArchiveOutputStream aos, File folder, String basePath,
                                         long totalSize, long processedSize, CompressCallback callback) throws Exception {
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    addFolderToArchive(aos, file, basePath + "/" + file.getName(), 
                                     totalSize, processedSize, callback);
                } else {
                    addFileToArchive(aos, file, basePath + "/" + file.getName(), 
                                   totalSize, processedSize, callback);
                }
            }
        }
    }

    private static void compressFilesTo7z(List<File> files, SevenZOutputFile sevenZOutput, 
                                        CompressCallback callback) throws Exception {
        long totalSize = getTotalSize(files);
        long processedSize = 0;

        for (File file : files) {
            if (file != null && file.exists()) {
                if (file.isDirectory()) {
                    addFolderTo7z(sevenZOutput, file, file.getName(), totalSize, processedSize, callback);
                } else {
                    addFileTo7z(sevenZOutput, file, file.getName(), totalSize, processedSize, callback);
                }
                processedSize += file.isDirectory() ? getFolderSize(file) : file.length();
            }
        }

        // 确保在压缩完成后调用成功回调
        if (callback != null) {
            callback.onSuccess("压缩完成");
        }
    }

    private static void addFileTo7z(SevenZOutputFile sevenZOutput, File file, String entryName,
                                  long totalSize, long processedSize, CompressCallback callback) throws Exception {
        SevenZArchiveEntry entry = sevenZOutput.createArchiveEntry(file, entryName);
        sevenZOutput.putArchiveEntry(entry);

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
            byte[] buffer = new byte[8192];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                sevenZOutput.write(buffer, 0, len);
                processedSize += len;
                
                if (totalSize > 0 && callback != null) {
                    int progress = (int) ((processedSize * 100) / totalSize);
                    callback.onProgress(Math.min(progress, 100));
                }
            }
        }
        sevenZOutput.closeArchiveEntry();
    }

    private static void addFolderTo7z(SevenZOutputFile sevenZOutput, File folder, String basePath,
                                    long totalSize, long processedSize, CompressCallback callback) throws Exception {
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    addFolderTo7z(sevenZOutput, file, basePath + "/" + file.getName(), 
                                 totalSize, processedSize, callback);
                } else {
                    addFileTo7z(sevenZOutput, file, basePath + "/" + file.getName(), 
                                totalSize, processedSize, callback);
                }
            }
        }
    }

    /**
     * 确保输出目录存在
     */
    private static void ensureOutputDir(File outputFile) {
        File parentDir = outputFile.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            parentDir.mkdirs();
        }
    }

    /**
     * 计算文件列表总大小
     */
    private static long getTotalSize(List<File> files) {
        long total = 0;
        for (File file : files) {
            if (file != null && file.exists()) {
                total += file.isDirectory() ? getFolderSize(file) : file.length();
            }
        }
        return total;
    }

    /**
     * 计算文件夹大小
     */
    private static long getFolderSize(File folder) {
        long size = 0;
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isFile()) {
                    size += file.length();
                } else if (file.isDirectory()) {
                    size += getFolderSize(file);
                }
            }
        }
        return size;
    }

    /**
     * 获取压缩文件扩展名
     */
    /**
     * 获取压缩格式对应的扩展名
     */
    public static String getExtension(CompressFormat format) {
        switch (format) {
            case ZIP:
                return ".zip";
            case SEVEN_Z:
                return ".7z";
            case TAR:
                return ".tar";
            case GZIP:
                return ".gz";
            case BZIP2:
                return ".bz2";
            case XZ:
                return ".xz";
            case AR:
                return ".ar";
            case CPIO:
                return ".cpio";
            case JAR:
                return ".jar";
            default:
                return ".zip";
        }
    }

    /**
     * 获取压缩格式描述
     */
    public static String getFormatDescription(CompressFormat format) {
        switch (format) {
            case ZIP:
                return "zip";
            case SEVEN_Z:
                return "7z";
            case TAR:
                return "tar";
            case GZIP:
                return "gzip";
            case BZIP2:
                return "bzip2";
            case XZ:
                return "xz";
            case AR:
                return "ar";
            case CPIO:
                return "cpio";
            case JAR:
                return "jar";
            default:
                return "未知格式";
        }
    }

    /**
     * 获取压缩模式描述
     */
    public static String getModeDescription(CompressMode mode) {
        switch (mode) {
            case FAST:
                return "速度快";
            case SMALL:
                return "体积小";
            default:
                return "默认";
        }
    }

    /**
     * 检查格式是否支持多文件压缩
     */
    public static boolean supportsMultipleFiles(CompressFormat format) {
        switch (format) {
            case GZIP:
            case BZIP2:
            case XZ:
                return false; // 这些格式只支持单个文件
            default:
                return true;
        }
    }
}